/*
 * Amadeus Confidential Information:
 * Unauthorized use and disclosure strictly forbidden.
 * @1998-2009 - Amadeus s.a.s - All Rights Reserved.
 */
package com.amadeus.spin.devtool.soap.shooter.process;

import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;
import org.w3c.dom.Node;

import com.amadeus.spin.devtool.soap.shooter.FatalException;
import com.amadeus.spin.devtool.soap.shooter.ProcessInput;
import com.amadeus.spin.devtool.soap.shooter.config.ConfigurationParameters;

/**
 * ReplaceSchema is called for each Soap request if Shooter has been ran with option -ver.
 * It replaces the schema version defined in the soap requests by the schema version given as a parameter
 *
 * @author DEV/SEP/QAD/FMT/MDE
 */
public class ReplaceSchemaVersion implements ProcessInput {
  private static Logger log = Logger.getLogger(ReplaceSchemaVersion.class);

  private static final String SCHEMA_ATTRIBUTE = "noNamespaceSchemaLocation";

  protected static final String VERSION_ATTRIBUTE = "Version=";

  // Index is used to ignore the first version="" representing the version of
  // the xml input
  private int fromIndex = 0;

  /**
   * Regexp to find version number, path prefix and path to schema :
   * - V(\\d+(?:\\.)?\\d+)[^-:/]* : search for the path before the version number
   * like http://nceetvdev58-4.nce.amadeus.net/xmlschemas/V
   * OR http://nceetvdev58-4.nce.amadeus.net/integration/V
   * OR http://nceetvdev58-4.nce.amadeus.net/integration/arv
   * - (?:\\d+(?:\\.)?\\d+(?:\\.)?\\d+) : Version number
   * - ([a-zA-Z]*) : String appended to the version number like "ARD"
   * - (/.*\\.xsd) : path to xsd
   */
  private static final Pattern PATTERN = Pattern.compile(
      "(.*V)(?:\\d+(?:\\.)?\\d+(?:\\.)?\\d?)([a-zA-Z]*\\d?)(/.*\\.xsd)", Pattern.CASE_INSENSITIVE);

  private static final Pattern PATTERN_VERSION = Pattern.compile("(.*V)(?:\\d+(?:\\.)?\\d+(?:\\.)?\\d*)(.*)",
      Pattern.CASE_INSENSITIVE);

  /**
   * Rewrites the url of the schema and the Version attribute
   * with the correct version.
   */
  @Override
  public String processInput(Stack<String> previousOutput, String input, Node config) throws FatalException {
    StringBuilder xml = new StringBuilder(input);

    String xmlSchemaUrl = null;
    String xmlVersionField = null;

    try {
      String overridenVersion = ConfigurationParameters.getCustomSchemaVersion();

      // Replace the version in the schema url
      xmlSchemaUrl = replaceschemaVersionURL(xml, overridenVersion);

      // Replace the Version attribute
      xmlVersionField = replaceVersionAttribute(xml, overridenVersion);

      return xml.toString();

    }
    catch (Exception e) {
      // reach condition : url or Version cannot be understood or any technical
      // exception - original url and Version are not modified
      StringBuilder sb = new StringBuilder("Unable to replace ");
      if (null == xmlSchemaUrl) {
        sb.append("Schema version in the schema url, attribute xsi:noNamespaceSchemaLocation is missing");
      }
      else if (null == xmlVersionField) {
        sb.append("Version attribute, Version attribute is missing");
        input = xml.toString();
      }
      else {
        sb.append("Schema version original URL : \"" + xmlSchemaUrl + "\" or Version attribute : \"" + xmlVersionField +
            "\"");
      }
      log.warn(sb.toString());
      return input;
    }
  }

  /**
   * Replaces the Version of the schema in the schema URL
   *
   * @param input
   * @param xml
   * @return
   */
  private String replaceschemaVersionURL(StringBuilder xml, String overridenVersion) {
    // Indexes for the schema field
    int start = xml.indexOf(SCHEMA_ATTRIBUTE);
    int end = -1;

    if (start > 0) {
      start = xml.indexOf("\"", start) + 1;
      end = xml.indexOf("\"", start);
      fromIndex = start + 1;
    }

    // Get the schema url to replace
    String xmlSchemaUrl = xml.substring(start, end);
    String cleanXmlSchemaUrl = xmlSchemaUrl.replaceAll("\\\\+", "/");

    // Apply pattern on original schema url to find version and schema name
    Matcher matcher = PATTERN.matcher(cleanXmlSchemaUrl);
    matcher.find();
    String prefixPath = matcher.group(1);
    String path = matcher.group(2);
    String suffix = matcher.group(3);

    // Generate the new schema url with the new version number
    StringBuilder targetSchemaUrl = new StringBuilder();

    targetSchemaUrl.append(prefixPath).append(overridenVersion).append(path).append(suffix);

    String targetUrl = targetSchemaUrl.toString();

    if (log.isInfoEnabled()) {
      log.info("Original schema version has been replaced ");
      log.info("  \"" + xmlSchemaUrl + "\"");
      log.info("  => \"" + targetUrl + "\"");
    }
    // Replace the schema url
    xml.replace(start, end, targetUrl);

    return xmlSchemaUrl;
  }

  private String replaceVersionAttribute(StringBuilder xml, String overridenVersion) {
    String newXmlVersionField = null;

    // Indexes for the version field
    int startVersion = xml.indexOf(VERSION_ATTRIBUTE, fromIndex);
    if (startVersion == -1) {
      // Cases where we had version instead of Version.
      startVersion = xml.indexOf(VERSION_ATTRIBUTE.toLowerCase(), fromIndex);
    }

    int endVersion = -1;
    if (startVersion > 0) {
      startVersion = xml.indexOf("\"", startVersion) + 1;
      endVersion = xml.indexOf("\"", startVersion);
    }

    // Construct the new Version field.
    String xmlVersionField = xml.substring(startVersion, endVersion);
    // Apply pattern on original version to get prefix and suffix of the number
    Matcher matcherVersion = PATTERN_VERSION.matcher(xmlVersionField);
    matcherVersion.find();
    String prefixVersion = matcherVersion.group(1);
    String postVersion = matcherVersion.group(2);

    newXmlVersionField = prefixVersion.concat(overridenVersion).concat(postVersion);

    if (log.isInfoEnabled()) {
      log.info("Original Version field has been replaced ");
      log.info("  \"" + VERSION_ATTRIBUTE + "\"" + xmlVersionField + "\"\"");
      log.info("  => \"" + VERSION_ATTRIBUTE + "\"" + newXmlVersionField + "\"\"");
    }

    // Replace the version field
    xml.replace(startVersion, endVersion, newXmlVersionField);
    return newXmlVersionField;
  }

}